﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace GraphAPI.Model
{
	public class Graph
	{
		public Graph()
		{
		}

		public Graph(Graph other)
		{
			foreach (Entity ent in other.Entitys)
			{
				entitys.Add(new Entity(ent));
			}

			foreach (Relation rel in other.Relations)
			{
				Relation newRel = new Relation(
					entitys[other.GetEntityId(rel.StartEntity)],
					entitys[other.GetEntityId(rel.EndEntity)],
					rel.Oriented, 
                    rel.Name);

				newRel.Name = rel.Name;
				newRel.Weight = rel.Weight;
				newRel.ColorMark = rel.ColorMark;
				
				relations.Add(newRel);
			}

			UpdateMatrixes();
		}

		public Rectangle Bounds
		{
			get
			{
				int minX = 0;
				int minY = 0;
				int maxX = 0;
				int maxY = 0;

				if (entitys.Count != 0)
					maxX = entitys[0].Position.X + entitys[0].Radius;

				if (entitys.Count != 0)
					minX = entitys[0].Position.X - entitys[0].Radius;

				if (entitys.Count != 0)
					maxY = entitys[0].Position.Y + entitys[0].Radius;

				if (entitys.Count != 0)
					minY = entitys[0].Position.Y - entitys[0].Radius;

				for (int i = 1; i < entitys.Count; i++)
				{
					if (minX > entitys[i].Position.X - entitys[i].Radius)
						minX = entitys[i].Position.X - entitys[i].Radius;

					if (minY > entitys[i].Position.Y - entitys[i].Radius)
						minY = entitys[i].Position.Y - entitys[i].Radius;

					if (maxX < entitys[i].Position.X + entitys[i].Radius)
						maxX = entitys[i].Position.X + entitys[i].Radius;

					if (maxY < entitys[i].Position.Y + entitys[i].Radius)
						maxY = entitys[i].Position.Y + entitys[i].Radius;
				}

				return new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1);
			}
		}

        public Entity FindEntity(Relation rel, Entity ent)
        {
            int relId = GetRelationId(rel);
            int entId = GetEntityId(ent);
            for (int i = 0; i < EntityCount; i++)
            {
                if (ent != entitys[i])
                    if (indentMatrix[relId, entId] != 0)
                        return entitys[i];
            }
            return null;
        }

        public Relation FindRelation(Entity from, Entity to)
		{
			int fromId = GetEntityId(from);
			int toId = GetEntityId(to);
			
			for (int i = 0; i < RelationCount; i++)
			{
				if (indentMatrix[toId, i] == 1 && indentMatrix[fromId, i] != 0)
					return relations[i];
			}
			return null;
		}

		private List<Entity> entitys = new List<Entity>();

		public Entity [] Entitys
		{
			get { return entitys.ToArray(); }
		}

		public int EntityCount
		{
			get
			{
				return entitys.Count;
			}
		}

        public int GetRelationId(Relation rel)
        {
            return relations.IndexOf(rel);
        }

		public int GetEntityId(Entity entity)
		{
			return entitys.IndexOf(entity);
		}

		public void AddEntity(Entity newEntity)
		{
			entitys.Add(newEntity);
			UpdateMatrixes();
		}

		public void RemoveEntity(Entity oldEntity)
		{
			entitys.Remove(oldEntity);
			UpdateMatrixes();
		}

		private List<Relation> relations = new List<Relation>();

		public Relation [] Relations
		{
			get { return relations.ToArray(); }
		}

		public int RelationCount
		{
			get
			{
				return relations.Count;
			}
		}

		public void AddRelation(Relation newRelation)
		{
			relations.Add(newRelation);
			UpdateMatrixes();
		}

		public void RemoveRelation(Relation oldRelation)
		{
			relations.Remove(oldRelation);
			UpdateMatrixes();
		}

		public void UpdateMatrixes()
		{
			relationMatrix = new bool[EntityCount, EntityCount];

			for (int i = 0; i < EntityCount; i++)
				for (int j = 0; j < EntityCount; j++)
					relationMatrix[i, j] = false;

			foreach (Relation relation in relations)
			{
				int from = GetEntityId(relation.StartEntity);
				int to = GetEntityId(relation.EndEntity);


				if (from == -1 || to == -1)
					continue;
                
                relationMatrix[from, to] = true;
                if (!relation.Oriented)
                    relationMatrix[to, from] = true;
    
			}

			indentMatrix = new int[EntityCount, RelationCount];

			for (int i = 0; i < RelationCount; i++)
				for (int j = 0; j < EntityCount; j++)
					indentMatrix[j, i] = 0;

			for (int iRelation = 0; iRelation < RelationCount; iRelation++)
			{
				Relation relation = relations[iRelation];
				int from = GetEntityId(relation.StartEntity);
				int to = GetEntityId(relation.EndEntity);
				if (from == -1 || to == -1)
					continue;

				if (relation.Oriented)
					indentMatrix[from, iRelation] = -1;
				else
					indentMatrix[from, iRelation] = 1;

				indentMatrix[to, iRelation] = 1;
			}

			//обновляем степень вершин
			foreach (Entity ent in Entitys)
			{
				ent.Degree = 0;

				foreach (Relation rel in Relations)
				{
					if (rel.StartEntity == ent || rel.EndEntity == ent)
						ent.Degree++;
				}
			}

		}

		private bool[,] relationMatrix;

		public bool[,] RelationMatrix
		{
			get { return (bool[,])relationMatrix.Clone(); }
		}

		private int[,] indentMatrix;

		public int[,] IntendMatrix
		{
			get { return (int[,])indentMatrix.Clone(); }
		}
	}
}
